﻿#light

namespace CodeBlock

open System
open System.Reflection

open Utils

//TODO: Relocation must be struct
type Relocation(offset: int, baseSymbol: string, size: int, ?absolute: bool)=
    let absolute = make_value absolute false
    
    member this.FixOffset with get() = offset
    member this.BaseSymbol with get() = baseSymbol
    member this.Absolute with get() = absolute
    member this.Size with get() = size
    
    member private this.SizeString with get() =
                                        match size with
                                        | 1 -> "byte"
                                        | 2 -> "word"
                                        | 4 -> "dword"
                                        | 8 -> "qword"
                                        | _ -> "int" + (size * 4).ToString()
    
    override this.ToString() =
        this.SizeString + (if baseSymbol = null then offset.ToString()
                              else
                                if absolute then
                                    "+" + baseSymbol + " + " + offset.ToString()
                                else
                                    baseSymbol + " + " + offset.ToString())
        

[<Sealed>]
type CodeBlock(binary: byte list, references: Relocation list, publicSymbols: Map<string, int>) =
    member this.Binary with get() = binary
    member this.References with get() = references
    member this.PublicSymbols with get() = publicSymbols
    
    member this.Merge (other: CodeBlock) =
        let l1 = this.Binary.Length
        let binary = List.append this.Binary other.Binary
        let fixReloc2 (reloc: Relocation) =
            Relocation(reloc.FixOffset + l1, reloc.BaseSymbol, reloc.Size, reloc.Absolute)
        let relocs = List.append this.References (List.map fixReloc2 other.References)
        let fixSymbols2 (name, pos) =
            name, pos + l1
        let symbols =
            Map.to_list other.PublicSymbols
            |> List.map fixSymbols2
            |> List.append (Map.to_list this.PublicSymbols)
            |> Map.of_list
        CodeBlock(binary, relocs, symbols)
